package com.rickpan.service;

import com.rickpan.dto.share.CreateShareRequest;
import com.rickpan.dto.share.UpdateShareRequest;
import com.rickpan.dto.share.ShareResponse;
import com.rickpan.dto.share.ShareAccessLogResponse;
import com.rickpan.entity.FileInfo;
import com.rickpan.entity.FileShare;
import com.rickpan.entity.ShareAccessLog;
import com.rickpan.entity.User;
import com.rickpan.exception.BusinessException;
import com.rickpan.repository.FileInfoRepository;
import com.rickpan.repository.FileShareRepository;
import com.rickpan.repository.ShareAccessLogRepository;
import com.rickpan.repository.UserRepository;
import com.rickpan.util.ShareCodeGenerator;
import com.rickpan.controller.FileShareController.ShareStatsResponse;
import com.rickpan.controller.FileShareController.ShareFilterParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import jakarta.persistence.criteria.Predicate;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 文件分享服务
 * 
 * @author RickPan Team
 * @since 2025-01-05
 */
@Service
public class FileShareService {

    private static final Logger log = LoggerFactory.getLogger(FileShareService.class);

    @Autowired
    private FileShareRepository fileShareRepository;

    @Autowired
    private FileInfoRepository fileInfoRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ShareAccessLogRepository shareAccessLogRepository;

    @Autowired
    private ShareCodeGenerator shareCodeGenerator;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Value("${app.base-url:http://localhost:5173}")
    private String baseUrl;

    /**
     * 创建文件分享
     */
    @Transactional
    public ShareResponse createShare(Long userId, CreateShareRequest request) {
        log.info("用户 {} 创建分享: fileId={}, shareType={}", userId, request.getFileId(), request.getShareType());

        // 1. 验证请求参数
        request.validate();

        // 2. 检查文件是否存在且用户有权限
        FileInfo fileInfo = validateFileAccess(userId, request.getFileId());

        // 3. 检查是否已经存在该文件的可用分享
        List<FileShare> userShares = fileShareRepository.findByFileId(request.getFileId())
                .stream()
                .filter(share -> share.getUserId().equals(userId) && share.getStatus() == FileShare.ShareStatus.ACTIVE)
                .collect(Collectors.toList());

        // 检查现有分享的可用性，自动更新过期状态
        Optional<FileShare> availableShare = Optional.empty();
        for (FileShare share : userShares) {
            if (share.isAvailable()) {
                // 找到可用的分享
                availableShare = Optional.of(share);
                break;
            } else if (share.isExpired()) {
                // 自动更新过期分享的状态
                share.setStatus(FileShare.ShareStatus.EXPIRED);
                fileShareRepository.save(share);
                log.info("自动更新过期分享状态: shareId={}, shareCode={}", share.getId(), share.getShareCode());
            }
        }

        if (availableShare.isPresent()) {
            log.info("文件 {} 已存在可用分享，返回现有分享", request.getFileId());
            return buildShareResponse(availableShare.get(), fileInfo);
        }

        // 4. 生成唯一分享码
        String shareCode = generateUniqueShareCode();

        // 5. 创建分享记录
        FileShare fileShare = new FileShare(
                shareCode,
                request.getFileId(),
                userId,
                request.getShareType(),
                null, // password 稍后设置
                request.getAllowDownload(),
                request.getAllowPreview(),
                request.getExpireTime(),
                request.getMaxAccessCount(),
                FileShare.ShareStatus.ACTIVE
        );

        // 6. 如果是密码保护，加密密码
        if (request.getShareType() == FileShare.ShareType.PASSWORD) {
            fileShare.setPassword(passwordEncoder.encode(request.getPassword()));
        }

        // 7. 设置分享备注
        fileShare.setDescription(request.getDescription());

        // 8. 保存分享记录
        fileShare = fileShareRepository.save(fileShare);

        log.info("分享创建成功: shareCode={}, fileId={}", shareCode, request.getFileId());
        return buildShareResponse(fileShare, fileInfo);
    }

    /**
     * 根据分享码获取分享信息
     */
    public ShareResponse getShareInfo(String shareCode) {
        log.debug("获取分享信息: shareCode={}", shareCode);

        FileShare fileShare = fileShareRepository.findByShareCode(shareCode)
                .orElseThrow(() -> new BusinessException("分享不存在"));

        // 检查分享状态
        if (!fileShare.isAvailable()) {
            throw new BusinessException("分享已失效");
        }

        // 获取文件信息
        FileInfo fileInfo = fileInfoRepository.findById(fileShare.getFileId())
                .orElseThrow(() -> new BusinessException("文件不存在"));

        return buildShareResponse(fileShare, fileInfo);
    }

    /**
     * 获取用户的分享列表
     */
    public Page<ShareResponse> getUserShares(Long userId, Pageable pageable) {
        log.debug("获取用户分享列表: userId={}", userId);

        Page<FileShare> sharePage = fileShareRepository.findByUserId(userId, pageable);

        return sharePage.map(share -> {
            FileInfo fileInfo = fileInfoRepository.findById(share.getFileId()).orElse(null);
            return buildShareResponse(share, fileInfo);
        });
    }

    /**
     * 获取用户的分享列表（带筛选）
     */
    public Page<ShareResponse> getUserSharesWithFilter(Long userId, Pageable pageable, ShareFilterParams filterParams) {
        log.debug("获取用户分享列表（带筛选）: userId={}, filter={}", userId, filterParams);

        Specification<FileShare> spec = createShareSpecification(userId, filterParams);
        Page<FileShare> sharePage = fileShareRepository.findAll(spec, pageable);

        return sharePage.map(share -> {
            FileInfo fileInfo = fileInfoRepository.findById(share.getFileId()).orElse(null);
            return buildShareResponse(share, fileInfo);
        });
    }

    /**
     * 删除分享
     */
    @Transactional
    public void deleteShare(Long userId, Long shareId) {
        log.info("删除分享: userId={}, shareId={}", userId, shareId);

        FileShare fileShare = fileShareRepository.findById(shareId)
                .orElseThrow(() -> new BusinessException("分享不存在"));

        // 检查权限
        if (!fileShare.getUserId().equals(userId)) {
            throw new BusinessException("无权限删除此分享");
        }

        fileShareRepository.delete(fileShare);
        log.info("分享删除成功: shareId={}", shareId);
    }

    /**
     * 更新分享设置
     */
    @Transactional
    public ShareResponse updateShare(Long userId, Long shareId, UpdateShareRequest request) {
        log.info("更新分享设置: userId={}, shareId={}", userId, shareId);

        FileShare fileShare = fileShareRepository.findById(shareId)
                .orElseThrow(() -> new BusinessException("分享不存在"));

        // 检查权限
        if (!fileShare.getUserId().equals(userId)) {
            throw new BusinessException("无权限修改此分享");
        }

        // 更新设置（只更新非空字段）
        if (request.getShareType() != null) {
            fileShare.setShareType(request.getShareType());
        }
        if (request.getAllowDownload() != null) {
            fileShare.setAllowDownload(request.getAllowDownload());
        }
        if (request.getAllowPreview() != null) {
            fileShare.setAllowPreview(request.getAllowPreview());
        }
        if (request.getExpireTime() != null) {
            fileShare.setExpireTime(request.getExpireTime());
        }
        if (request.getMaxAccessCount() != null) {
            fileShare.setMaxAccessCount(request.getMaxAccessCount());
        }

        // 更新密码
        if (request.getShareType() != null) {
            if (request.getShareType() == FileShare.ShareType.PASSWORD) {
                if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
                    fileShare.setPassword(passwordEncoder.encode(request.getPassword()));
                }
            } else {
                fileShare.setPassword(null);
            }
        }

        fileShare = fileShareRepository.save(fileShare);

        FileInfo fileInfo = fileInfoRepository.findById(fileShare.getFileId()).orElse(null);
        return buildShareResponse(fileShare, fileInfo);
    }

    /**
     * 获取分享统计信息
     */
    public ShareStatsResponse getShareStats(Long userId) {
        log.debug("获取分享统计信息: userId={}", userId);

        // 获取用户所有分享
        List<FileShare> allShares = fileShareRepository.findByUserId(userId);

        long totalShares = allShares.size();
        long activeShares = allShares.stream()
                .filter(share -> share.getStatus() == FileShare.ShareStatus.ACTIVE && !share.isExpired())
                .count();
        long expiredShares = allShares.stream()
                .filter(FileShare::isExpired)
                .count();

        // 获取访问统计
        List<Long> shareIds = allShares.stream().map(FileShare::getId).toList();
        long totalAccess = shareIds.isEmpty() ? 0 : shareAccessLogRepository.countByShareIdIn(shareIds);

        // 获取今日访问统计
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        LocalDateTime todayEnd = todayStart.plusDays(1);
        long todayAccess = shareIds.isEmpty() ? 0 :
                shareAccessLogRepository.countByShareIdInAndAccessTimeBetween(shareIds, todayStart, todayEnd);

        // 获取下载和预览统计
        long downloadCount = shareIds.isEmpty() ? 0 :
                shareAccessLogRepository.countByShareIdInAndActionType(shareIds, ShareAccessLog.ActionType.DOWNLOAD);
        long previewCount = shareIds.isEmpty() ? 0 :
                shareAccessLogRepository.countByShareIdInAndActionType(shareIds, ShareAccessLog.ActionType.PREVIEW);

        return new ShareStatsResponse(totalShares, activeShares, expiredShares,
                totalAccess, todayAccess, downloadCount, previewCount);
    }

    /**
     * 禁用分享
     */
    @Transactional
    public void disableShare(Long userId, Long shareId) {
        log.info("禁用分享: userId={}, shareId={}", userId, shareId);

        FileShare fileShare = fileShareRepository.findById(shareId)
                .orElseThrow(() -> new BusinessException("分享不存在"));

        // 检查权限
        if (!fileShare.getUserId().equals(userId)) {
            throw new BusinessException("无权限操作此分享");
        }

        fileShare.setStatus(FileShare.ShareStatus.DISABLED);
        fileShareRepository.save(fileShare);

        log.info("分享禁用成功: shareId={}", shareId);
    }

    /**
     * 启用分享
     */
    @Transactional
    public void enableShare(Long userId, Long shareId) {
        log.info("启用分享: userId={}, shareId={}", userId, shareId);

        FileShare fileShare = fileShareRepository.findById(shareId)
                .orElseThrow(() -> new BusinessException("分享不存在"));

        // 检查权限
        if (!fileShare.getUserId().equals(userId)) {
            throw new BusinessException("无权限操作此分享");
        }

        fileShare.setStatus(FileShare.ShareStatus.ACTIVE);
        fileShareRepository.save(fileShare);

        log.info("分享启用成功: shareId={}", shareId);
    }

    /**
     * 获取分享访问日志
     */
    public Page<ShareAccessLogResponse> getShareAccessLogs(Long userId, Long shareId, Pageable pageable) {
        log.debug("获取分享访问日志: userId={}, shareId={}", userId, shareId);

        // 验证分享是否属于当前用户
        FileShare fileShare = fileShareRepository.findById(shareId)
                .orElseThrow(() -> new BusinessException("分享不存在"));

        if (!fileShare.getUserId().equals(userId)) {
            throw new BusinessException("无权限查看此分享的访问日志");
        }

        // 获取访问日志
        Page<ShareAccessLog> logPage = shareAccessLogRepository.findByShareIdOrderByAccessTimeDesc(shareId, pageable);

        return logPage.map(log -> new ShareAccessLogResponse(
                log.getId(),
                log.getShareId(),
                log.getVisitorIp(),
                log.getVisitorUserAgent(),
                log.getActionType().name(),
                log.getAccessTime()
        ));
    }

    // 私有辅助方法

    private FileInfo validateFileAccess(Long userId, Long fileId) {
        FileInfo fileInfo = fileInfoRepository.findById(fileId)
                .orElseThrow(() -> new BusinessException("文件不存在"));

        if (!fileInfo.getOwnerId().equals(userId)) {
            throw new BusinessException("无权限分享此文件");
        }

        if (fileInfo.getIsDeleted()) {
            throw new BusinessException("文件已删除，无法分享");
        }

        return fileInfo;
    }

    private String generateUniqueShareCode() {
        String shareCode;
        int attempts = 0;
        do {
            shareCode = shareCodeGenerator.generateShareCode();
            attempts++;
            if (attempts > 10) {
                throw new BusinessException("生成分享码失败，请重试");
            }
        } while (fileShareRepository.existsByShareCode(shareCode));

        return shareCode;
    }

    private ShareResponse buildShareResponse(FileShare fileShare, FileInfo fileInfo) {
        String fileName = fileInfo != null ? fileInfo.getOriginalName() : "未知文件";
        
        // 获取分享者用户名
        String userName = userRepository.findById(fileShare.getUserId())
                .map(User::getUsername)
                .orElse("未知用户");

        return ShareResponse.fromEntity(fileShare, fileName, userName, baseUrl);
    }

    /**
     * 创建分享查询规范
     */
    private Specification<FileShare> createShareSpecification(Long userId, ShareFilterParams filterParams) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            // 用户ID筛选
            predicates.add(criteriaBuilder.equal(root.get("userId"), userId));

            // 关键词筛选（文件名）
            if (StringUtils.hasText(filterParams.getKeyword())) {
                // 需要关联查询文件信息表
                predicates.add(criteriaBuilder.like(
                    criteriaBuilder.lower(root.get("fileName")),
                    "%" + filterParams.getKeyword().toLowerCase() + "%"
                ));
            }

            // 状态筛选
            if (StringUtils.hasText(filterParams.getStatus()) && !"all".equals(filterParams.getStatus())) {
                switch (filterParams.getStatus()) {
                    case "active":
                        predicates.add(criteriaBuilder.equal(root.get("status"), FileShare.ShareStatus.ACTIVE));
                        predicates.add(criteriaBuilder.or(
                            criteriaBuilder.isNull(root.get("expireTime")),
                            criteriaBuilder.greaterThan(root.get("expireTime"), LocalDateTime.now())
                        ));
                        break;
                    case "expired":
                        predicates.add(criteriaBuilder.and(
                            criteriaBuilder.isNotNull(root.get("expireTime")),
                            criteriaBuilder.lessThanOrEqualTo(root.get("expireTime"), LocalDateTime.now())
                        ));
                        break;
                    case "disabled":
                        predicates.add(criteriaBuilder.equal(root.get("status"), FileShare.ShareStatus.DISABLED));
                        break;
                }
            }

            // 日期范围筛选
            if (StringUtils.hasText(filterParams.getStartDate())) {
                try {
                    LocalDateTime startDate = LocalDate.parse(filterParams.getStartDate(), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
                    predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
                } catch (Exception e) {
                    log.warn("解析开始日期失败: {}", filterParams.getStartDate());
                }
            }

            if (StringUtils.hasText(filterParams.getEndDate())) {
                try {
                    LocalDateTime endDate = LocalDate.parse(filterParams.getEndDate(), DateTimeFormatter.ISO_LOCAL_DATE).atTime(23, 59, 59);
                    predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));
                } catch (Exception e) {
                    log.warn("解析结束日期失败: {}", filterParams.getEndDate());
                }
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
    }
}
